#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include <CoOS.h>			 			/*!< CooCox RTOS header file.		  */

#include "ir_remo_ctrl.h"


#include "Driver\DrvGPIO.h"
#include "Driver\DrvUSB.h"
#include "USB\VCOMSys.h"

#include "usb_vcom.h"

#include "task_shell.h"

#include "temperature.h"
#include "command_test.h"
#include "flash_param.h"


// Mutex for print
OS_MutexID		g_mut_usb_print;			//




/* Bus event call back functions */
S_DRVUSB_EVENT_PROCESS g_sBusOps[6] = 
{
	{NULL, NULL},                 		/* Attach event callback        */
	{UsbOnDetach, NULL},                        /* Detach event callback        */
	{DrvUSB_BusResetCallback, &gsVcomDevice},   /* Bus reset event callback     */
	{NULL, NULL},                               /* Bus suspend event callback   */
	{NULL, NULL},                               /* Bus resume event callback    */
	{DrvUSB_CtrlSetupAck, &gsVcomDevice},       /* Setup event callback         */
};



//-- USB descriptors
#define CTRL_EP_NUM         0
#define INT_IN_EP_NUM       3   
//#define BULK_OUT_EP_NUM     2
#define BULK_IN_EP_NUM      1

#define USB_VID			0x0416  /* Vendor ID */
#define USB_PID			0x5011  /* Product ID */


#define USB_VID_LO      (USB_VID & 0xFF)
#define USB_VID_HI      ((USB_VID >> 8) & 0xFF)

#define USB_PID_LO      (USB_PID & 0xFF)
#define USB_PID_HI      ((USB_PID >> 8) & 0xFF)


//volatile uint16_t g_tx_size = 0;

const uint8_t gau8StringSerial[26] =
{
	26,				/* bLength          */
	DESC_STRING,	/* bDescriptorType  */
	'N', 0, 
	'T', 0, 
	'2', 0, 
	'0', 0, 
	'0', 0, 
	'9', 0, 
	'1', 0, 
	'0', 0, 
	'1', 0, 
	'4', 0, 
	'0', 0, 
	'0', 0
};


const uint8_t gau8StringLang[] = {
	4,				/* bLength                  */
	DESC_STRING,	/* bDescriptorType          */
	0x09, 0x04      /* Language ID: USA(0x0409) */
};

const uint8_t gau8VendorStringDescriptor[] = {
	16,             /* bLength          */
	DESC_STRING,    /* bDescriptorType  */
	'N', 0, 
	'u', 0, 
	'v', 0, 
	'o', 0, 
	't', 0, 
	'o', 0, 
	'n', 0
};

const uint8_t gau8ProductStringDescriptor[] = {
	32,             /* bLength          */
	DESC_STRING,    /* bDescriptorType  */
	'U', 0, 
	'S', 0,
	'B', 0,
	' ', 0,
	'V', 0,
	'i', 0,
	'r', 0,
	't', 0,
	'u', 0,
	'a', 0,
	'l', 0,
	' ', 0,
	'C', 0,
	'O', 0,
	'M', 0
};

const uint8_t gau8DeviceDescriptor[] =
{
	LEN_DEVICE,		/* bLength              */
	DESC_DEVICE,	/* bDescriptorType      */
	0x00, 0x02,		/* bcdUSB               */
	0x02,			/* bDeviceClass         */
	0x00,			/* bDeviceSubClass      */
	0x00,			/* bDeviceProtocol      */
	BULK_EP_MXPLD,	/* bMaxPacketSize0      */
	USB_VID_LO,                             
	USB_VID_HI,     /* Veondor ID           */
	USB_PID_LO,     
	USB_PID_HI,     /* Product ID           */
	0x00, 0x03,		/* bcdDevice            */
	0x01,			/* iManufacture         */
	0x02,			/* iProduct             */
	0x03,			/* iSerialNumber        */
	0x01			/* bNumConfigurations   */
};


const uint8_t gau8ConfigDescriptor[] =
{
	LEN_CONFIG,		/* bLength              */
	DESC_CONFIG,	/* bDescriptorType      */
	0x43, 0x00,		/* wTotalLength         */
	0x02,			/* bNumInterfaces       */
	0x01,			/* bConfigurationValue  */
	0x00,			/* iConfiguration       */
	0xC0,			/* bmAttributes         */
	0x32,			/* MaxPower             */

	/* INTERFACE descriptor */
	LEN_INTERFACE,	/* bLength              */
	DESC_INTERFACE,	/* bDescriptorType      */
	0x00,			/* bInterfaceNumber     */
	0x00,			/* bAlternateSetting    */
	0x01,			/* bNumEndpoints        */
	0x02,			/* bInterfaceClass      */
	0x02,			/* bInterfaceSubClass   */
	0x01,			/* bInterfaceProtocol   */
	0x00,			/* iInterface           */

	/* Communication Class Specified INTERFACE descriptor */
    0x05,           /* Size of the descriptor, in bytes */
    0x24,           /* CS_INTERFACE descriptor type */
    0x00,           /* Header functional descriptor subtype */
    0x10, 0x01,     /* Communication device compliant to the communication spec. ver. 1.10 */
    
	/* Communication Class Specified INTERFACE descriptor */
    0x05,           /* Size of the descriptor, in bytes */
    0x24,           /* CS_INTERFACE descriptor type */
    0x01,           /* Call management functional descriptor */
    0x00,           /* BIT0: Whether device handle call management itself. */
                    /* BIT1: Whether device can send/receive call management information over a Data Class Interface 0 */
    0x01,           /* Interface number of data class interface optionally used for call management */

	/* Communication Class Specified INTERFACE descriptor */
    0x04,           /* Size of the descriptor, in bytes */
    0x24,           /* CS_INTERFACE descriptor type */
    0x02,           /* Abstract control management funcational descriptor subtype */
    0x00,           /* bmCapabilities       */
    
	/* Communication Class Specified INTERFACE descriptor */
    0x05,           /* bLength              */
    0x24,           /* bDescriptorType: CS_INTERFACE descriptor type */
    0x06,           /* bDescriptorSubType   */
    0x00,           /* bMasterInterface     */
    0x01,           /* bSlaveInterface0     */
    
	/* ENDPOINT descriptor */
	LEN_ENDPOINT,	                /* bLength          */
	DESC_ENDPOINT,	                /* bDescriptorType  */
	(EP_INPUT | INT_IN_EP_NUM),     /* bEndpointAddress */
	EP_INT,		                    /* bmAttributes     */
	INT_EP_MXPLD, 0x00,	            /* wMaxPacketSize   */
	0x01,	                        /* bInterval        */
			
	/* INTERFACE descriptor */
	LEN_INTERFACE,	/* bLength              */
	DESC_INTERFACE,	/* bDescriptorType      */
	0x01,			/* bInterfaceNumber     */
	0x00,			/* bAlternateSetting    */
	0x02,			/* bNumEndpoints        */
	0x0A,			/* bInterfaceClass      */
	0x00,			/* bInterfaceSubClass   */
	0x00,			/* bInterfaceProtocol   */
	0x00,			/* iInterface           */
			
	/* ENDPOINT descriptor */
	LEN_ENDPOINT,	                /* bLength          */
	DESC_ENDPOINT,	                /* bDescriptorType  */
	(EP_INPUT | BULK_IN_EP_NUM),	/* bEndpointAddress */
	EP_BULK,		                /* bmAttributes     */
	BULK_EP_MXPLD, 0x00,	        /* wMaxPacketSize   */
	0x00,			                /* bInterval        */

	/* ENDPOINT descriptor */
	LEN_ENDPOINT,	                /* bLength          */
	DESC_ENDPOINT,	                /* bDescriptorType  */
	(EP_OUTPUT | BULK_OUT_EP_NUM),	/* bEndpointAddress */
	EP_BULK,		                /* bmAttributes     */
	BULK_EP_MXPLD, 0x00,	        /* wMaxPacketSize   */
	0x00,			                /* bInterval        */
};


static OS_FlagID	g_flag_usb_attached = E_INVALID_ID;
static OS_MutexID	g_mut_usb_print = E_CREATE_FAIL;			//

static void RoughDelay(uint32_t t)
{
    volatile int32_t delay;
    delay = t;
    while(delay-- >= 0);
}





//--

void UsbOnAttach(void * pdata)
{
//-- Defination
	E_DRVUSB_STATE eUsbState;
	eUsbState = DrvUSB_GetUsbState();

	CoEnterISR();
//	if ( DrvUSB_GetUsbState() != eDRVUSB_DETACHED )
	if ( (eUsbState & eDRVUSB_ATTACHED) )
	{
		isr_SetFlag(g_flag_usb_attached);
	}
	CoExitISR();
}


static OS_EventID	g_queid_usbrx = E_CREATE_FAIL;


void UsbOnDetach(void * pdata)
{
//-- Defination
	E_DRVUSB_STATE eUsbState;
	//<DEBUG>
	eUsbState = DrvUSB_GetUsbState();
	if ( eDRVUSB_DETACHED == eUsbState )
	{
		// let GetChar() go
		CoEnterISR();
		isr_PostQueueMail(g_queid_usbrx, (void*)(0xFFFFFFFF) );
		isr_PostQueueMail(g_queid_usbrx, NULL);
		CoExitISR();
	}

}



//-- USB transmit data handling 
static OS_FlagID	g_flag_usbtx = 0;

void VCOM_BulkInAckCallback(void* pVoid)
{
	/* Reserved for user define */
	CoEnterISR();
	isr_SetFlag(g_flag_usbtx);
	CoExitISR();
}

void VCOM_IntInAckCallback(void* pVoid)
{
    uint8_t au8Buf[INT_EP_MXPLD] = {0};
    /* Reserved for user define */
    DrvUSB_DataIn(INT_IN_EP_NUM, au8Buf, INT_EP_MXPLD);
}

void SendChar(int ch)
{
	static uint8_t usbtx_buf[BULK_EP_MXPLD] = {0};
	static uint16_t cur_ptr = 0;	

	usbtx_buf[cur_ptr++] = ch;
	if ( cur_ptr >= BULK_EP_MXPLD || '\n' == ch || '\0' == ch )
	{
		DrvUSB_DataIn(BULK_IN_EP_NUM, usbtx_buf, cur_ptr);	

		CoWaitForSingleFlag(g_flag_usbtx, 0);
		cur_ptr=0;
	}
}


int usb_printf(const char * format, ...)
{
//-- Defination
	int ir = 0;
	va_list arg_list;

//-- Code
	CoEnterMutexSection(g_mut_usb_print);
	va_start(arg_list, format); 
	ir = vprintf(format, arg_list);
	CoLeaveMutexSection(g_mut_usb_print);		
	return ir;
}

//-- USB receive data handling 

// Queue for receive data from USB host
#define		QUE_SIZE_OUTDATA	(4)
void *		g_que_usbrx[QUE_SIZE_OUTDATA];
							 
void VCOM_BulkOutAckCallback(void* pVoid)
{
//-- Definations
	uint8_t * buffer;
	uint32_t size;
	StatusType rs;
		
//-- Codes
	buffer = DrvUSB_GetOutData(BULK_OUT_EP_NUM, &size);
	CoEnterISR();
	rs = isr_PostQueueMail(g_queid_usbrx, buffer);
	rs = isr_PostQueueMail(g_queid_usbrx, (void*)(size));
	//<ERROR>
	CoExitISR();
}

char GetChar(void)
{
//-- Definations
	static uint8_t * cur_buf = NULL;
	static uint32_t remain = 0;

	StatusType	ir = E_OK;
	char ch;

//-- Code
	if (NULL == cur_buf)
	{
		cur_buf = (uint8_t*)( CoPendQueueMail(g_queid_usbrx, 0, &ir) );
		if ( E_OK != ir )	{	/*<ERROR>*/		}

		remain = (uint32_t)( CoPendQueueMail(g_queid_usbrx, 0, &ir) );
		if ( E_OK != ir ) 	{	/*<ERROR>*/		}
	}

	if ( cur_buf == (void*)(0xFFFFFFFF) ) 
	{
		cur_buf = NULL;
		remain = 0;
		return 0;
	}

	ch = *(cur_buf);
	--remain;

	if (remain <= 0)
	{
		cur_buf = NULL;
		remain = 0;
		DrvUSB_DataOutTrigger(BULK_OUT_EP_NUM, BULK_EP_MXPLD);
	}
	return ch;
}





static CMD_DEF 	command_define[] = {
//	{"tm", 0, OnTimerTest},
//	{"st", 4, SetTimer0},
	{"pd", 0, PowerDownTest},
//	{"sg", 0, OnSetGpio},
	{"rf", 0, read_all_parameter},

 	{"tm", 0, read_temperature},
	{"sc", 0, StartCalibration},
	{"ec", 0, EndCalibration},
	{"vx", 0, OnInformation},

	{"wt", 8, set_parameter_temp},
	{"wf", 4, set_parameter_fan},
	{"sp", 0, save_parameter},
	{"ep", 0, reset_all_scale},

	{"rp", 0, read_parameter},
	{"fn", 2, SetFanPower},

	{0, 0, NULL},  				// m_handle == NULL means end of table
};

static SHELL_CREATE_CONTEXT	shell_context;

//-- task usb listen
OS_STK   	task_usb_listen_Stk[TASK_STK_SIZE];	 	/*!< Stack of 'task_init' task.	  */

void task_usb_listen(void * pdata)
{
//-- Defination
	E_DRVUSB_STATE eUsbState;

//-- Code
	// Initialize context for shell
	memset(&shell_context, 0, sizeof(shell_context));
	shell_context.m_get_ch_func = GetChar;
	shell_context.m_cmd_tab = command_define;

	eUsbState = DrvUSB_GetUsbState();		//<DEBUG>

	g_queid_usbrx = CoCreateQueue(
		g_que_usbrx, QUE_SIZE_OUTDATA, EVENT_SORT_TYPE_FIFO);
	if (E_CREATE_FAIL == g_queid_usbrx)
	{
		//<ERROR>
	}
	
	g_flag_usbtx = CoCreateFlag(1, 0);	
	//<ERROR>
	 
	
	g_flag_usb_attached = CoCreateFlag(TRUE, 0);

	/* Enable Interrupt and install the call back function */
    DrvUSB_Open((void*)DrvUSB_DispatchEvent);
	VCOM_Open();

    /* Enable Interrupt and install the call back function */
	eUsbState = DrvUSB_GetUsbState();

	if (eUsbState >= eDRVUSB_ATTACHED)
    {
        /* Force Bus Reset for 150 ms*/
        _DRVUSB_ENABLE_SE0();
        RoughDelay(150000);
        _DRVUSB_DISABLE_SE0();
    }
    		
	/* Disable USB-related interrupts. */
	_DRVUSB_ENABLE_MISC_INT(0);

	/* Enable float-detection interrupt. */
	_DRVUSB_ENABLE_FLDET_INT();
        
	_DRVUSB_ENABLE_MISC_INT(INTEN_WAKEUP | INTEN_WAKEUPEN | INTEN_FLDET | INTEN_USB | INTEN_BUS);

	g_sBusOps[0].apfnCallback = UsbOnAttach;

	eUsbState = DrvUSB_GetUsbState();
	if ( (eUsbState & eDRVUSB_ATTACHED) ) 	CoSetFlag(g_flag_usb_attached);


	while (1)
	{
		CoWaitForSingleFlag(g_flag_usb_attached, 0);
		//CoCreateTask(task_shell, (void*)0, SHELL_PRI, &task_shell_Stk[TASK_STK_SIZE-1], TASK_STK_SIZE);
		create_shell(&shell_context);
		//<ERROR>

	}
}


